Generated code - Transactions, SelfServicing
	Preface
	LLBLGen already had a strong transaction support and in LLBLGen Pro this tradition is continued. The generated framework supports both
	native database transactions and COM+ transactions. Both types are described below as well as how you can include transactional behaviour
	in your own code.
 
	
Normal native database transactions
	(It's assumed the database used supports transactions, which is the case in all major databases like SqlServer).
	Native database transactions are provided by ADO.NET; it's a part of an ADO.NET connection object and that transaction object
	can be used to execute all database statements in that transaction if that connection is used.
	LLBLGen Pro's native database transactions and also the COM+ transactions work the same for you: you create an instance of 
	the transaction object with the type you want (COM+ or normal) and add the objects that should participate (use) that
	transaction to that transaction object. As of that moment the actions you perform on those objects are executed in the
	transaction of that transaction object. 
	
	An example will help illustrate the usage of the Transaction object. Let's add a new order with an order row to the persistent
	storage for the customer "CHOPS". Because we'll add two entities, we will use a transaction to make sure that when the second
	save fails, the first is rolled back. 
	
	
    	
        	  Note:
         | 
    
    
        | 
        	The example below is just to show you how to use the Transaction object as it doesn't use
	the recursive save functionality build into the code. As recursive saves already use an ADO.NET transaction internally, the example would have been
	much smaller without the external transaction. To illustrate this, the same example is also listed using recursive saves. It also doesn't use the
	FK-PK synchronization functionality which synchronizes Foreign key fields with their Primary key field's value, after a save, for example
	when newOrderRow is added to newOrder.OrderDetails, which will sync the new PK value of newOrder.OrderID with newOrderRow.OrderID once newOrder
	has been saved. See the recursive save example below for an example of that.
         | 
    
	First, let's see the code: (it assumes the entity data for "CHOPS" is already loaded in
	the object customer. The data is rather bogus, it's for illustration purposes only).
  
  
    // [C#]
// Create the transaction object, pass the isolation level and give it a name
Transaction transactionManager = new Transaction(IsolationLevel.ReadCommitted, "Test");
// create a new order and then 2 new order rows. 
try
{
	// create new order entity. Use data from the object 'customer'
	OrderEntity newOrder = new OrderEntity();
	
	// set the customer reference, which will sync FK-PK values.
	// (newOrder.CustomerID = customer.CustomerID)
	newOrder.Customer = customer;
	
	newOrder.EmployeeID = 1;
	newOrder.Freight = 10;
	newOrder.OrderDate = DateTime.Now.AddDays(-3.0);
	newOrder.RequiredDate = DateTime.Now.AddDays(3.0);
	newOrder.ShipAddress = customer.Address;
	newOrder.ShipCity = customer.City;
	newOrder.ShipCountry = customer.Country;
	newOrder.ShipName = "The Bounty";
	newOrder.ShippedDate = DateTime.Now;
	newOrder.ShipRegion = customer.Region;
	newOrder.ShipVia = 1;
	newOrder.ShipPostalCode = customer.PostalCode;
	// add this new order to the transaction so actions will run inside the transaction
	transactionManager.Add(newOrder);
	// save the new order. When this fails, will throw exception which will terminate transaction.
	newOrder.Save();
	// Create new order row.
	OrderDetailsEntity newOrderRow = new OrderDetailsEntity();
	newOrderRow.OrderID = newOrder.OrderID;	// will refetch order from persistent storage.
	newOrderRow.Discount = 0;
	newOrderRow.ProductID = 10;
	newOrderRow.Quantity = 200;
	newOrderRow.UnitPrice = 31;
	// add this new orderrow to the transaction
	transactionManager.Add(newOrderRow);
	// save the new orderrow. When this fails, will throw exception which will terminate transaction.
	newOrderRow.Save();
	// done, commit the transaction
	transactionManager.Commit();
}
catch(Exception)
{
	// abort, roll back the transaction
	transactionManager.Rollback();
	throw;
}
finally
{
	// clean up. Necessary action.
	transactionManager.Dispose();
} 
    ' [VB.NET]
' Create the transaction object, pass the isolation level and give it a name
Dim transactionManager As new Transaction(IsolationLevel.ReadCommitted, "Test")
' create a new order and then 2 new order rows. 
Try
	' create new order entity. Use data from the object 'customer'.
	Dim newOrder As New OrderEntity()
	
	' set the customer reference, which will sync FK-PK values 
	' (newOrder.CustomerID = customer.CustomerID)
	newOrder.Customer = customer
	
	newOrder.EmployeeID = 1
	newOrder.Freight = 10
	newOrder.OrderDate = DateTime.Now.AddDays(-3.0)
	newOrder.RequiredDate = DateTime.Now.AddDays(3.0)
	newOrder.ShipAddress = customer.Address
	newOrder.ShipCity = customer.City
	newOrder.ShipCountry = customer.Country
	newOrder.ShipName = "The Bounty"
	newOrder.ShippedDate = DateTime.Now
	newOrder.ShipRegion = customer.Region
	newOrder.ShipVia = 1
	newOrder.ShipPostalCode = customer.PostalCode
	' add this new order to the transaction so actions will run inside the transaction
	transactionManager.Add(newOrder)
	' save the new order. When this fails, will throw exception which will terminate transaction.
	newOrder.Save()
	' Create new order row.
	Dim newOrderRow As New OrderDetailsEntity()
	newOrderRow.OrderID = newOrder.OrderID	' will refetch order from persistent storage.
	newOrderRow.Discount = 0
	newOrderRow.ProductID = 10
	newOrderRow.Quantity = 200
	newOrderRow.UnitPrice = 31
	' add this new orderrow to the transaction
	transactionManager.Add(newOrderRow)
	' save the new orderrow. When this fails, will throw exception which will terminate transaction.
	newOrderRow.Save()
	' done, commit the transaction
	transactionManager.Commit()
Catch
	' abort, roll back the transaction
	transactionManager.Rollback()
	Throw
Finally
	' clean up. Necessary action.
	transactionManager.Dispose()
End Try
 
   
 
	First a transaction object is created. As soon as you instantiate the object, a database connection is open and usable. This is
	also the reason why you 
have to include a finally clause and have to call Dispose() when the transaction is no longer needed.
	If you want an entity or entity collection object (and all its objects inside it) to participate in a transaction, add it to
	the particular transaction object with the Add() method. You can only add objects that can execute data modification statements
	like an entity object or an entity collection object, however you can't add a typed list or typed view since these objects can only
	read data and for reading data, transactions are not interesting.
	
	When you add an entity or entity collection object to a transaction object, from that moment on its logic will use the connection of
	the transaction object to work with the database, which automatically makes sure the database statements executed will be run inside
	the transaction itself. The transaction object will take care of every other overhead like notifying objects when a transaction has
	been finished or aborted. If a transaction is aborted (Rollback() is called) or Committed (Commit() is called), all objects participating
	in the transaction are automatically removed from the transaction so you don't have to do that yourself. 
	
	It's best practise to embed the usage of the transaction in a try/catch/finally statement as it is done in the example above, so you can 
	make sure when something fails during the usage of the transaction: everything is rolled back or at the end and everything is committed
	correctly. 
	
	
Using recursive save functionality
	The same functionality, but now implemented using the recursive save functionality build into the generated code. As you can see, no external
	transaction is used, because the code starts a new transaction internally. You can always add the entities to an existing transaction, which
	then makes the save actions take place inside that transaction. Recursive saves always create a normal ADO.NET transaction with isolation level
	ReadComitted. 
	
	If you want to use a COM+ transaction with recursive saves, you have to start one by instantiating a TransactionComPlus object and then by adding 
	the entity you want to save to that TransactionComPLus object.
  
  
    // [C#]
// create a new order and then 2 new order rows. 
// create new order entity. Use data from the object 'customer'
OrderEntity newOrder = new OrderEntity();
// set the customer reference, which will sync FK-PK values.
// (newOrder.CustomerID = customer.CustomerID). You also could have said:
// newOrder.CustomerID = customer.CustomerID;
// or
// newOrder.CustomerID = _someVariable;
newOrder.Customer = customer;
newOrder.EmployeeID = 1;
newOrder.Freight = 10;
newOrder.OrderDate = DateTime.Now.AddDays(-3.0);
newOrder.RequiredDate = DateTime.Now.AddDays(3.0);
newOrder.ShipAddress = customer.Address;
newOrder.ShipCity = customer.City;
newOrder.ShipCountry = customer.Country;
newOrder.ShipName = "The Bounty";
newOrder.ShippedDate = DateTime.Now;
newOrder.ShipRegion = customer.Region;
newOrder.ShipVia = 1;
newOrder.ShipPostalCode = customer.PostalCode;
// Create new order row.
OrderDetailsEntity newOrderRow = new OrderDetailsEntity();
newOrderRow.Discount = 0;
newOrderRow.ProductID = 10;
newOrderRow.Quantity = 200;
newOrderRow.UnitPrice = 31;
// make sure the OrderID fields are synchronized when
// newOrder is saved.
newOrder.OrderDetails.Add(newOrderRow);
// save the new order, recursively. This will first save customer
// if that's changed, then newOrder, then sync newOrder.OrderID with newOrderRow.OrderID
// and then save newOrderRow. The complete Save action is done inside an ADO.NET transaction.
newOrder.Save(true);
 
    ' [VB.NET]
' create a new order and then 2 new order rows. 
' create new order entity. Use data from the object 'customer'
Dim newOrder As New OrderEntity()
' set the customer reference, which will sync FK-PK values.
' (newOrder.CustomerID = customer.CustomerID). You also could have said:
' newOrder.CustomerID = customer.CustomerID
' or
' newOrder.CustomerID = _someVariable
newOrder.Customer = customer
newOrder.EmployeeID = 1
newOrder.Freight = 10
newOrder.OrderDate = DateTime.Now.AddDays(-3.0)
newOrder.RequiredDate = DateTime.Now.AddDays(3.0)
newOrder.ShipAddress = customer.Address
newOrder.ShipCity = customer.City
newOrder.ShipCountry = customer.Country
newOrder.ShipName = "The Bounty"
newOrder.ShippedDate = DateTime.Now
newOrder.ShipRegion = customer.Region
newOrder.ShipVia = 1
newOrder.ShipPostalCode = customer.PostalCode
' Create new order row.
Dim newOrderRow As New OrderDetailsEntity()
newOrderRow.Discount = 0
newOrderRow.ProductID = 10
newOrderRow.Quantity = 200
newOrderRow.UnitPrice = 31
' make sure the OrderID fields are synchronized when
' newOrder is saved.
newOrder.OrderDetails.Add(newOrderRow)
' save the new order, recursively. This will first save customer
' if that's changed, then newOrder, then sync newOrder.OrderID with newOrderRow.OrderID
' and then save newOrderRow. The complete Save action is done inside an ADO.NET transaction.
newOrder.Save(True)
 
   
 
	
Transaction savepoints
	Most databases support transaction savepoints. Transaction savepoints make it possible to do fine grained transaction control on a semi-nested level. This can
	be required as ADO.NET doesn't support nested transactions. Savepoints let you define a point in a transaction to which you can roll back, without rolling back
	the complete transaction. This can be handy if you have saved some entities in a transaction which were saved OK, and another one fails, however the failure of that
	save shouldn't terminate the whole transaction, just roll back the transaction to a given point in the transaction. LLBLGen Pro offers you the ability to define
	savepoints in a transaction. The following example illustrates the savepoint functionality. It first saves a new address entity and after that it saves the
	transaction. It then saves a new customer entity but takes into account that this can fail. If it does, it should roll back to the savepoint set, it should thus
	not rollback the complete transaction. Consider the example an illustration for the feature, in your code, the code utilizing the transaction will probably span
	several classes and methods. Savepoints are not supported with COM+ transactions.
  
  
    //C#
' Create the transaction object, pass the isolation level and give it a name
Transaction transactionManager = new Transaction(IsolationLevel.ReadCommitted, "SavepointRollback");
try
{
	// first save a new address
	AddressEntity newAddress = new AddressEntity();
	// ... fill the address entity with values
	transactionManager.Add(newAddress);
	
	// save it.
	newAddress.Save();
	// save the transaction
	transactionManager.Save("SavepointAddress");
	// save a new customer
	CustomerEntity newCustomer = new CustomerEntity();
	// ... fill the customer entity with values
	newCustomer.VisitingAddress = newAddress;
	newCustomer.BillingAddress = newAddress;
	
	transactionManager.Add(newCustomer);
	
	try
	{
		newCustomer.Save();
	}
	catch(Exception ex)
	{
		// something was wrong. 
		// ... handle ex here.
		// roll back to savepoint.
		transactionManager.Rollback("SavepointAddress");
	}
	
	// commit the transaction. If the customer save failed, 
	// only address is saved, otherwise both.
	transactionManager.Commit();
}
catch
{
	// fatal error, roll back everything
	transactionManager.Rollback();
	throw;
}
 
    ' VB.NET
' Create the transaction object, pass the isolation level and give it a name
Dim transactionManager As new Transaction(IsolationLevel.ReadCommitted, "SavepointRollback")
Try
	' first save a new address
	Dim newAddress As New AddressEntity()
	' ... fill the address entity with values
	transactionManager.Add(newAddress)
	
	' save it.
	newAddress.Save()
	' save the transaction
	transactionManager.Save("SavepointAddress")
	' save a new customer
	Dim newCustomer As New CustomerEntity()
	' ... fill the customer entity with values
	newCustomer.VisitingAddress = newAddress
	newCustomer.BillingAddress = newAddress
	
	transactionManager.Add(newCustomer)
		
	Try
		newCustomer.Save()
	Catch(Exception ex)
		' something was wrong. 
		' ... handle ex here.
		' roll back to savepoint.
		transactionManager.Rollback("SavepointAddress")
	End Try
	
	' commit the transaction. If the customer save failed, 
	' only address is saved, otherwise both.
	transactionManager.Commit()
Catch
	// fatal error, roll back everything
	transactionManager.Rollback()
	Throw
End Try
 
   
 
	
	
    	
        	  Note:
         | 
    
    
        | 
        	 Microsoft Access doesn't support savepoints in transactions, so this feature is not supported when you use LLBLGen Pro with MS Access.
	
         | 
    
	
	COM+ transactions
	LLBLGen Pro supports COM+ transactions as well through a special transaction class TransactionComPlus, instead of the normal class Transaction.
	Because COM+ is implemented in .NET using Enterprise Services, the class 
using the TransactionComPlus object has to derive from
	ServicedComponent. This way, transactions started outside the class using the TransactionComPlus class can flow through to the TransactionComPlus
	class. Also, you have to reference the System.EnterpriseServices namespace in your code. Below is a short example how a COM+ transaction
	flows through to the ServicedComponent derived class and the code inside the TestComPlus() method will be running inside the COM+ transaction. When no
	COM+ transaction is available, a new one is created.
	
	
    	
        	  Note:
         | 
    
    
        | 
        	COM+ transactions are considered 'advanced material' in .NET applications. Use them with care. You have to give your assemblies a strong name and
	the code will cause some overhead on your system: it has a context inside COM+, which is a native windows service. Most of the time you can
	fulfill your transactional requirements using native database transactions with the normal Transaction class in the generated code, as
	illustrated in the previous section. 
         | 
    
  
  
    // [C#]
[Transaction(TransactionOption.Required)]
public class TestClass : ServicedComponent
{
	[AutoComplete]
	public void TestComPlus()
	{
		CustomerEntity customer = new CustomerEntity("CHOPS");
		OrderEntity order = new OrderEntity(10254);
		TransactionComPlus comPlusTransaction = new TransactionComPlus();
		comPlusTransaction.Add(customer);
		comPlusTransaction.Add(order);
		try
		{
			customer.Country="Brazil";
			customer.Save();
			order.ShipName="The WaveKiller";
			order.Save();
			comPlusTransaction.Commit();
		}
		catch
		{
			// abort
			comPlusTransaction.Rollback();
			// bubble exception OR call setAbort on current context.
			throw;
		}
		finally
		{
			comPlusTransaction.Dispose();
		}
	}
} 
    ' [VB.NET]
<Transaction(TransactionOption.Required)> _
Public Class TestClass 
	Inherits ServicedComponent
	<AutoComplete> _
	Public Sub TestComPlus()
		Dim customer As New CustomerEntity("CHOPS")
		Dim order As New OrderEntity(10254)
		Dim comPlusTransaction As New TransactionComPlus()
		comPlusTransaction.Add(customer)
		comPlusTransaction.Add(order)
		Try
			customer.Country="Brazil"
			customer.Save()
			order.ShipName="The WaveKiller"
			order.Save()
			comPlusTransaction.Commit()
		Catch
			' abort
			comPlusTransaction.Rollback()
			' bubble exception OR call setAbort on current context.
			Throw
		Finally
			comPlusTransaction.Dispose()
		End Try
	End Sub
End Class 
   
 
	The TestComPlus method will now anticipate in an existing COM+ transaction and will, if the method succeeds, automatically commit
	the transaction, provided the method doesn't throw an exception. If it does, the transaction is considered aborted and will roll back.
	
	.NET 2.0: System.Transactions support
	.NET 2.0 introduces the System.Transactions namespace. This is a namespace with the TransactionScope class, which eases the creation of distributed transactions,
	specifying a given scope. All transactions, for example normal ADO.NET transactions, are automatically elevated to distributed transactions, if required, by the 
	TransactionScope they're declared in. This requires support by the used database system as the database system has to be able to 
promote a non-distributed
	transaction to a distributed transaction. When .NET 2.0 shipped, only SqlServer 2005 was able to promote transactions to distributed transactions using
	System.Transactions' classes.
	
	
	The developer can define such a TransactionScope using the normal .NET constructs, like 
using(TransactionScope scope = new TransactionScope())
{
	// your code here.
}
	It's recommended to read the System.Transactions documentation in the MSDN documentation archive for further background info on this new namespace and its classes.
	
	An LLBLGen Pro Transaction object is able to determine if it's participating inside an ambient transaction of
	System.Transactions. If so, it enlists a Resource Manager with the System.Transactions transaction. The Resource manager contains the 
	DataAccessAdapter object. As soon as a Transaction or DataAccessAdapter is enlisted through a Resource Manager, the Commit() and Rollback() methods
	are setting the ResourceManager's commit/abort signal which is requested by the System.Transactions' Transaction manager. If multiple transactions are executed 
	on a DataAccessAdapter and one rolled back,	the resource manager will report an abort. As soon as the DataAccessAdapter is enlisted in the
	System.Transactions.Transaction, no ADO.NET transaction is
	started, it's a no-op. Once one rollback is requested, the transaction will always report a rollback to the MSDTC.	
	
	
Going out of scope
	When the System.Transactions transaction is committed or rolled back, the Resource manager is notified and will then notify the Transaction object (if any) 
	that it can commit/rollback the transaction. That call will then notify the enlisted entities of the outcome of the transaction.
	
Example
	Below is an example which shows the usage of a TransactionScope in combination of a Transaction object. The code contains Assert statements to
	illustrate the state / outcome of the various statements.
  
  
     // C#
CustomerEntity newCustomer = new CustomerEntity();
// fill newCustomer's fields.
// ..
AddressEntity newAddress = new AddressEntity();
// fill newAddress' fields.
// ..
// start the scope.
using( TransactionScope ts = new TransactionScope() )
{
	// start a new LLBLGen Pro transaction
	using( Transaction trans = new Transaction(System.Data.IsolationLevel.ReadCommitted, "Test") )
	{
		newCustomer.VisitingAddress = newAddress;
		newCustomer.BillingAddress = newAddress;
		// add the entities to save to the LLBLGen Pro transaction
		trans.Add( newCustomer );
		trans.Add( newAddress );
		// save both entities.
		Assert.IsTrue(newCustomer.Save( true ));
	}
	// do not call Complete, as we want to rollback the transaction and see if the rollback indeed succeeds.
	// as the TransactionScope goes out of scope, the on-going transaction is rolled back.
}
// at this point the transaction of the previous using block is rolled back.
// let the DTC and the system.transactions threads deal with the objects. 
// this sleep is only needed because we're going to access the data directly after the rollback. In normal code, 
// this sleep isn't necessary.
Thread.Sleep( 1000 );
// test if the data is still there. Shouldn't be as the transaction has been rolled back. 
CustomerEntity fetchedCustomer = new CustomerEntity( customerId );
Assert.AreEqual( EntityState.New, fetchedCustomer.Fields.State );
AddressEntity fetchedAddress = new AddressEntity( addressId);
Assert.AreEqual( EntityState.New, fetchedAddress.Fields.State ); 
     'VB.NET 
Dim NewCustomer As New CustomerEntity()
' fill NewCustomer's fields.
' ..
Dim NewAddress As New AddressEntity()
' fill NewAddress' fields.
' ..
' start the scope.
Using  ts As New TransactionScope() 
	' start a New LLBLGen Pro transaction
	Using trans As New Transaction(System.Data.IsolationLevel.ReadCommitted, "Test") 
		NewCustomer.VisitingAddress = NewAddress
		NewCustomer.BillingAddress = NewAddress
		' add the entities to save to the LLBLGen Pro transaction
		trans.Add( NewCustomer )
		trans.Add( NewAddress )
		' save both entities.
		Assert.IsTrue(NewCustomer.Save( True ))
	End Using
	' do not call Complete, as we want to rollback the transaction and see if the rollback indeed succeeds.
	' as the TransactionScope goes out of scope, the on-going transaction is rolled back.
End Using
' at this point the transaction of the previous using block is rolled back.
' let the DTC and the system.transactions threads deal with the objects. 
' this sleep is only needed because we're going to access the data directly after the rollback. In normal code, 
' this sleep isn't necessary.
Thread.Sleep( 1000 )
' test if the data is still there. Shouldn't be as the transaction has been rolled back. 
Dim fetchedCustomer As New CustomerEntity( customerId )
Assert.AreEqual( EntityState.New, fetchedCustomer.Fields.State )
Dim fetchedAddress As New AddressEntity( addressId)
Assert.AreEqual( EntityState.New, fetchedAddress.Fields.State )